/**
 * Created by Administrator on 2017/1/15.
 */

var Room = require('../../game/room').Room;

var MJ = require('./MJ');
var PublicCards = require('./publicCards').PublicCards;
var HangupTask = require('./hangupTask').HangupTask;
var SubCode = require('../subCode');


exports.MJRoom = Room.extend({
    _publicCards: null,        // 公牌
    _hangupTask: null,         // 被挂起的任务
    _selectColorStatus: MJ.SelectColorStatus.NONE,  // 定缺状态

    _dealerIndex: 0,           // 庄家坐位
    _dealerLZ: 0,              // 连庄次数

    _curPlay: 0,               // 当前出牌玩家坐位
    _curTask: 0,               // 当前玩家操作
    _curGang: 0,               // 当前玩家连续杠的次数
    _curPlayNewCard: false,

    _lastPlayIndex: 0,        // 上次出牌玩家坐位
    _lastPlayCardIndex: 0,   // 上次出牌的有效牌的玩家坐位（碰/杠/吃/胡 不算）

    _huTasks: [],               // 已经胡牌的玩家胡牌事件

    init: function (rid, creator, msg) {
        this._super(rid, creator, msg);

        this._publicCards = new PublicCards(this);
        this._hangupTask = new HangupTask(this);
        this._selectColorStatus = MJ.SelectColorStatus.NONE;

        this._dealerIndex = 1;
        this._dealerLZ = 1;

        this._curPlay = 0;
        this._curTask = 0;
        this._curGang = 0;
        this._curPlayNewCard = false;

        this._lastPlayIndex = 0;
        this._lastPlayCardIndex = 0;

        this._huTasks = [];
    },

    checkInit: function () {
        if (!this._super()) {
            return false;
        }

        return true;
    },

    getPublicCards: function () {
        return this._publicCards;
    },

    getDealerPlayer: function () {
        return this.getPlayerByIndex(this._dealerIndex);
    },

    getDealerIndex: function () {
        return this._dealerIndex;
    },

    getDealerLZ: function () {
        return this._dealerLZ;
    },

    getCurPlay: function () {
        return this._curPlay;
    },

    getCurPlayer: function () {
        return this.getPlayerByIndex(this._curPlay);
    },

    getCurTask: function () {
        return this._curTask;
    },

    getCurGang: function () {
        return this._curGang;
    },

    getHangupTask: function () {
        return this._hangupTask;
    },

    /**
     * 就否黄庄，仅在结算时有用
     * @returns {boolean}
     */
    isRoundHZ: function () {
        return this.getHuPlayerCount() == 0;
    },

    getHuPlayerCount: function () {
        return this._huTasks.length;
    },

    getFirstHuTask: function () {
        return this._huTasks.length > 0 ? this._huTasks[0] : null;
    },

    getFirstHuPlayerIndex: function () {
        return this._huTasks.length > 0 ? this._huTasks[0].playerIndex : 0;
    },

    getFirstDPIndex: function () {
        var dpIndex = 0;
        this._huTasks.forEach(function (huTask) {
            if (!dpIndex && huTask.playerIndex != huTask.from) {
                dpIndex = huTask.from;
            }
        });
        return dpIndex;
    },

    isSelectColorFinished: function () {
        return this._selectColorStatus == MJ.SelectColorStatus.FINISHED;
    },

    isSelectColor: function () {
        return this.getSubConfig().selectColor;
    },

    hangupEmptyHandler: function () {

    },

    /******************************************************************
     * 重载的接口
     ******************************************************************/

    getRoomConfig: function () {
        var cfg = this._super();
        cfg.MJsettlementWait = 1000;    // 结算等待时长
        return cfg;
    },

    getInfo: function () {
        var info = this._super();
        info.dealer = this._dealerIndex;
        info.dealerLZ = this._dealerLZ;
        return info;
    },

    getRoundInfo: function () {
        var info = this._super();
        info.dealer = this._dealerIndex;
        info.dealerLZ = this._dealerLZ;
        return info;
    },

    getReconnectInfo: function (rePlayer) {
        var info = this._super(rePlayer);
        info.selectColorStatus = this._selectColorStatus;
        info.dealer = this._dealerIndex;
        info.dealerLZ = this._dealerLZ;
        info.curPlay = this.getCurPlay();
        info.curTask = this.getCurTask();
        info.curPlayNewCard = this._curPlayNewCard;
        info.publicCardNum = this._publicCards.getRemainNum();

        info.lastPlayIndex = this._lastPlayIndex;
        info.lastPlayCardIndex = this._lastPlayCardIndex;

        if (this._hangupTask.exist()) {
            info.hangupTaskSource = clone(this._hangupTask.source);
        }

        info.huTasks = this._huTasks;

        return info;
    },

    getSettlementInfo: function () {
        var info = this._super();
        info.isHZ = this.isRoundHZ(); // 是否黄庄家
        info.dealerIndex = this.getDealerIndex();
        info.dealerLZ = this.getDealerLZ();
        return info;
    },

    checkStartNewRound: function () {
        return this._super();
    },

    onPlayerReconnect: function (player) {
        this._super(player);
    },

    onPlayerEnter: function (player) {
        this._super(player);
        player.setWaitHandleTime(this.getNowTime());
    },

    onBeforePlayerReady: function (player) {
        return this._super(player);
    },

    onAfterPlayerReady: function (player) {
        this._super(player);
        player.setWaitHandleTime(0);
    },

    onAfterChangeCurPlay: function (oldCurPlay, newCard) {

        if (oldCurPlay != this.getCurPlay()) {
            // 换人出牌，清除杠上杠信息
            this.enumPlayers(function (eIndex, ePlayer) {
                ePlayer.resetPrevGang();
            }.bind(this));
        }
    },

    onAfterNewCard: function () {

    },

    onBeforeStartNewRound: function () {
        this._super();

        this._publicCards.gen();
        this._hangupTask.reset();
        this._selectColorStatus = MJ.SelectColorStatus.NONE;
        this._curPlay = 0;
        this._curTask = 0;
        this._curGang = 0;
        this._curPlayNewCard = false;

        this._lastPlayIndex = 0;
        this._lastPlayCardIndex = 0;

        this._huTasks = [];
    },

    onCurRoundFinished: function () {
        this._super();
    },

    onDestroy: function () {
        this._super();
    },

    //检测杠后发牌的处理
    onCheckGangNewCard: function (isQG, gangInfo, player) {
        if (isQG) {
            if (this.checkGangNewCard(gangInfo)) {
                logger.debug("room=%d sendHangupTask in=MJ.dealHangupTaskGang checkGangTaskQG=true checkGangNewCard=true", this.rid());
                this._sendHangupTask(this._dealNewCard, [player]);
            } else {
                logger.debug("room=%d sendHangupTask in=MJ.dealHangupTaskGang checkGangTaskQG=true checkGangNewCard=false", this.rid());
                this._sendHangupTask(this._doCheckCurPlayHangupTask);
            }
        } else {
            if (this.checkGangNewCard(gangInfo)) { // 没有则发摸牌
                this._dealNewCard(player);
            } else {
                this._doCheckCurPlayHangupTask();
            }
        }
    },

    /******************************************************************
     * 需要重载的接口
     ******************************************************************/

    /**
     * 是否允许一炮多响
     */
    isYPDXEnabled: function () {
        return (this.getSubConfig().disableYPDX) ? false : true;
    },

    /***
     * 胡了之后是否可以继续游戏
     * @param player
     * @returns {boolean}
     */
    isAfterHuContinuePlay: function (player) {
        return !player.isRoundHu();
    },

    /**
     * 开始一局
     */
    startPlay: function () {

    },

    /**
     * 定缺前检测
     * @param player
     * @param msg
     * @returns {Number} 错误码
     */
    onBeforeSelectColor: function (player, msg) {

    },

    /**
     * 出牌前检测
     * @param player
     * @param msg
     * @returns {Number} 错误码
     */
    onBeforePlayCard: function (player, msg, card) {
        if (this.isSelectColor()) {
            if (!this.isSelectColorFinished()) {
                return SubCode.MJ_SELECT_COLOR_STATUS_ERROR;
            }

            if (MJ.getColor(card) != player.getSelectColor() && MJ.existColor(player.getHandCards(), player.getSelectColor())) {
                return SubCode.MJ_EXIST_SELECTED_COLOR;
            }
        }

        if (this.isCurRoundFinished()) {
            return Code.ROOM_ROUND_FINISHED;
        }
    },

    /**
     * 出牌后处理
     * @param player 出牌玩家
     * @param card 出的牌
     * @param playInfo 出牌信息将会广播给客户端，修改此对象可定制出牌返给客户端的消息
     */
    onAfterPlayCard: function (player, card, playInfo) {
    },

    /**
     * 杠后处理
     * @param player 杠牌的玩家
     * @param card 杠的那张牌
     * @param gangTask 杠牌事件
     * @param gangInfo 杠牌信息
     */
    onAfterGangTask: function (player, card, gangTask, gangInfo) {
        var gang = gangInfo.gang;
        player.pushPregGang(gang);
        return true;
    },

    /**
     * 碰后处理
     */
    onAfterPengTask: function (player, card, pengTask) {

    },

    /**
     * 吃后处理
     */
    onAfterChiTask: function (player, card, chiTask) {

    },

    /**
     * 自定义挂起任务
     * @param player
     * @param taskInfo
     */
    onCustomHangupTask: function (player, taskInfo) {

    },

    /**
     * 摸牌前处理
     */
    onBeforeNewCard: function () {

    },

    /**
     * 检查当前玩家的挂起任务
     */
    checkCurPlayHangupTask: function () {

    },

    /**
     * 检查摸牌后玩家的挂起任务
     */
    checkNewCardHangupTask: function () {

    },

    /**
     * 检查出牌后其它玩家的挂起任务
     * @param card 当前出的牌
     */
    checkPlayCardHangupTask: function (card) {

    },

    /**
     * 检查杠牌后的挂起任务
     * @param playerIndex
     * @param card
     */
    checkGangHangupTask: function (playerIndex, card) {

    },

    /**
     * 检查是否可抢杠
     * @param task
     * @param gangInfo
     */
    checkGangTaskQG: function (task, gangInfo) {
        return false;
    },

    /**
     * 检查杠后是否需要发牌
     * @param gangInfo
     */
    checkGangNewCard: function (gangInfo) {
        return true;
    },

    /**
     * 检查胡牌后是否可能结算
     */
    checkHuSettlement: function () {

    },

    /**
     * 胡牌处理
     * @param task
     */
    onSettlementHuTask: function (task) {

    },

    /******************************************************************
     * 操作接口
     ******************************************************************/

    /**
     * 定缺
     * @param player
     * @param msg {color:花色}
     * @param callback
     */
    handleSelectColor: function (player, msg, callback) {
        var subConfig = this.getSubConfig();
        if (!subConfig.selectColor) {
            callback({code: SubCode.MJ_SELECT_COLOR_NOT_ALLOWED});
            return;
        }

        if (this.isSelectColorFinished()) {
            callback({code: SubCode.MJ_SELECT_COLOR_STATUS_ERROR});
            return;
        }

        if (isNaN(msg.color)) {
            callback({code: Code.ARGS_INVALID});
            return;
        }

        var color = +msg.color;
        if (subConfig.colors.indexOf(color) == -1) {
            callback({code: Code.ARGS_VALUE_WRONG});
            return;
        }

        if (player.isSelectColor()) {
            callback({code: Code.MJ_SELECT_COLOR_STATUS_ERROR});
            return;
        }

        var code = this.onBeforeSelectColor(player, msg);
        if (code) {
            callback({code: code});
            return;
        }

        player.setSelectColor(color);
        player.setWaitHandleTime(0);
        callback();

        this.broadcastReplay("onPlayerSelectColor", {
            playerIndex: player.getIndex(),
        });

        var isAllSelected = true;
        var playerColors = {};
        this.enumPlaying(function (eIndex, ePlayer) {
            playerColors[eIndex] = ePlayer.getSelectColor();
            if (!ePlayer.isSelectColor()) {
                isAllSelected = false;
            }
        });

        if (isAllSelected) {
            this.broadcast("onPlayerColorSelected", {"players": playerColors});

            this._selectColorStatus = MJ.SelectColorStatus.FINISHED;
            this.startPlay();
        }
    },

    handleQueryTings: function (player, msg, callback) {
        if (!this.isPlaying()) {
            callback({code: Code.ROOM_NOT_PLAYING});
            return;
        }

        callback({tings: player.getTingCards()});
    },

    handleBaoTing: function (player, msg, callback) {
        if (!this.isPlaying()) {
            callback({code: Code.ROOM_NOT_PLAYING});
            return;
        }

        var playerIndex = player.getIndex();
        var taskInfo = this._hangupTask.getPlayerTask(playerIndex, MJ.Task.TING);
        if (!taskInfo || this._hangupTask.isCommitted(playerIndex)) {
            callback({code: SubCode.TASK_UNMATCHED});
            return;
        }

        callback({});
        player.setWaitHandleTime(0);

        if (this.getSubConfig().taskTingPriority) {
            taskInfo.playerIndex = playerIndex;
            this.onHangupTask(MJ.Task.TING, [taskInfo]);
            this._hangupTask.submitPass(playerIndex);
        } else {
            this._hangupTask.submitTask(player.getIndex(), MJ.Task.TING);
        }
    },

    handleCheckAndBaoTing: function (player, msg, callback) {
        if (!this.isPlaying()) {
            callback({code: Code.ROOM_NOT_PLAYING});
            return;
        }

        var tings = player.checkTing();
        if (tings.length == 0) {
            callback({code: SubCode.TASK_UNMATCHED});
            return;
        }

        var cardNum = ExObject.sumValue(player.getHandCards());
        if (!cardNum || (cardNum % 3 != 1)) {
            callback({code: SubCode.MJ_NOT_TING});
            return;
        }

        player.setBaoTing();
        player.setTingCards(tings);
        player.setWaitHandleTime(0);
        this.broadcastReplay("onPlayerBaoTing", {
            playerIndex: player.getIndex(),
        });
    },

    handlePlayCard: function (player, msg, callback) {
        if (!this.isPlaying()) {
            callback({code: Code.ROOM_NOT_PLAYING});
            return;
        }

        if (player.getIndex() != this.getCurPlay()) {
            callback({code: SubCode.NOT_CUR_PLAY});
            return;
        }

        if (this._hangupTask.existOther(player.getIndex())) {
            callback({code: SubCode.EXIST_OTHER_HANGUP});
            return;
        }

        if (isNaN(msg.card)) {
            callback({code: Code.ARGS_INVALID});
            return;
        }

        var card = +msg.card;
        var code = this.onBeforePlayCard(player, msg, card);
        if (code) {
            callback({code: code});
            return;
        }

        if (!player.playCard(card)) {
            callback({code: SubCode.CARD_NOT_EXIST});
            return;
        }

        this._lastPlayIndex = this.getCurPlay();
        callback({});
        player.setWaitHandleTime(0);

        if (msg.ting) {
            var room = this;
            this.handleCheckAndBaoTing(player, msg, function (resp) {
                logger.debug("room=%d index=%d play and ting result:%j", room.rid(), player.getIndex(), resp);
            });
        }

        this._curTask = MJ.Task.PLAY;

        this._hangupTask.reset();
        this.checkPlayCardHangupTask(card);

        var playInfo = {
            playerIndex: player.getIndex(),
            card: card
        };

        if (this._hangupTask.exist()) {
            playInfo.existTask = true;
        }

        this.onAfterPlayCard(player, card, playInfo);
        this.broadcastReplay("onPlayCard", playInfo);

        logger.debug("room=%d sendHangupTask in=MJ.handlePlayCard", this.rid());
        this._sendHangupTask(this._nextCurPlay);
    },

    handleHuTask: function (player, msg, callback) {
        if (!this.isPlaying()) {
            callback({code: Code.ROOM_NOT_PLAYING});
            return;
        }

        if (!this._hangupTask.existTask(player.getIndex(), MJ.Task.HU)) {
            callback({code: SubCode.TASK_UNMATCHED});
            return;
        }
        callback({});
        player.setWaitHandleTime(0);
        if (!this.isYPDXEnabled()) {
            var temptask = this._hangupTask.getPlayerTask(player.getIndex(), MJ.Task.HU);
            var distance = this.getIndexDistance(temptask.from, player.getIndex());
            this._hangupTask.submitTask(player.getIndex(), MJ.Task.HU);
            console.log(distance)
            var huPlayers = [];
            this._hangupTask.enumUncommitted(function (index, player) {
                if (this._hangupTask.getPlayerTask(index, MJ.Task.HU)) {
                    huPlayers.push(index);
                }
            }.bind(this));
            console.log('huplayers')
            console.log(huPlayers)
            if (huPlayers.length && huPlayers.every(function (v) {
                    console.log(temptask.from);
                    console.log(v);
                    return this.getIndexDistance(temptask.from, v) > distance
                }.bind(this))) {
                var passList = [];
                this._hangupTask.enumUncommitted(function (index, player) {
                    passList.push(index);
                });
                console.log(passList)
                passList.forEach(function (v) {
                    this._hangupTask.submitPass(v);
                }.bind(this));
            }
            return;
        }
        this._hangupTask.submitTask(player.getIndex(), MJ.Task.HU);
    },

    handlePengTask: function (player, msg, callback) {
        if (!this.isPlaying()) {
            callback({code: Code.ROOM_NOT_PLAYING});
            return;
        }

        if (!this._hangupTask.existTask(player.getIndex(), MJ.Task.PENG)) {
            callback({code: SubCode.TASK_UNMATCHED});
            return;
        }
        callback({});
        player.setWaitHandleTime(0);
        this._hangupTask.submitTask(player.getIndex(), MJ.Task.PENG);
    },

    handleGangTask: function (player, msg, callback) {
        if (!this.isPlaying()) {
            callback({code: Code.ROOM_NOT_PLAYING});
            return;
        }

        if (!this._hangupTask.existTask(player.getIndex(), MJ.Task.GANG)) {
            callback({code: SubCode.TASK_UNMATCHED});
            return;
        }
        callback({});
        player.setWaitHandleTime(0);

        this._hangupTask.submitTask(player.getIndex(), MJ.Task.GANG, msg.selectGang);
    },

    handleChiTask: function (player, msg, callback) {
        if (!this.isPlaying()) {
            callback({code: Code.ROOM_NOT_PLAYING});
            return;
        }

        if (!this._hangupTask.existTask(player.getIndex(), MJ.Task.CHI)) {
            callback({code: SubCode.TASK_UNMATCHED});
            return;
        }
        callback({});
        player.setWaitHandleTime(0);

        this._hangupTask.submitTask(player.getIndex(), MJ.Task.CHI, +msg.selectCard);
    },

    handlePassTask: function (player, msg, callback) {
        if (!this.isPlaying()) {
            callback({code: Code.ROOM_NOT_PLAYING});
            return;
        }

        if (!this._hangupTask.exist(player.getIndex())) {
            callback({code: SubCode.TASK_UNMATCHED});
            return;
        }
        callback({});
        player.setWaitHandleTime(0);

        if (this.getSubConfig().checkPassHu) {
            if (this._hangupTask.existTask(player.getIndex(), MJ.Task.HU) && this.getCurPlay() != player.getIndex()) {
                player.setPassHu(true);
            }
        }

        this._hangupTask.submitPass(player.getIndex());
    },

    handleGmSetPublicCards: function (player, msg, callback) {
        if (!player.isSuperGM()) {
            callback({code: Code.GM_LEVEL_LIMIT});
            return;
        }

        this._publicCards.setRemainCards(msg.cards);
        callback({});
    },

    handleGmGetPublicCards: function (player, msg, callback) {
        if (!player.isSuperGM()) {
            callback({code: Code.GM_LEVEL_LIMIT});
            return;
        }

        callback({"cards": this._publicCards.getRemainCards()});
    },


    /******************************************************************
     * 功能接口
     ******************************************************************/
    /**
     * 给玩家发牌
     */
    initPlayersHandCards: function (startIndex) {
        this.enumPlayers(function (eIndex, ePlayer) {
            ePlayer.initHandCards(this._publicCards.getCards(ePlayer.getInitHandCardNeedNum(), ePlayer.getInitHandCardPreCards()));
        }.bind(this), startIndex);

        this._sendPublicCardChanged();
    },

    /**
     * 挂起事件处理回调
     * @param task 事件类型
     * @param arg1
     * @param arg2
     */
    onHangupTask: function (task, arg1, arg2) {
        switch (task) {
            case MJ.Task.HU:
                this.dealHangupTaskHu(arg1);
                break;
            case MJ.Task.GANG:
                this.dealHangupTaskGang(arg1);
                break;
            case MJ.Task.PENG:
                this.dealHangupTaskPeng(arg1);
                break;
            case MJ.Task.CHI:
                this.dealHangupTaskChi(arg1);
                break;
            case MJ.Task.TING:
                this.dealHangupTaskTing(arg1);
                break;
            case MJ.Task.CUSTOM:
                this.dealHangupTaskCustom(arg1);
                break;
            case MJ.Task.PASS:
                if (arg1) {
                    arg1.apply(this, arg2);
                }
                break;
            default:
                break;
        }
    },

    /**
     * (可能导致挂起事件的)操作后的挂起事件处理结束回调
     * @param source
     * @param taskId
     */
    onHangupTaskDone: function (source, taskId) {
        source.doneTaskId = taskId;
        this.broadcast("onHangupTaskDone", source);

        if (source.task == MJ.Task.PLAY) {
            if (taskId != MJ.Task.PENG && taskId != MJ.Task.GANG && taskId != MJ.Task.HU && taskId != MJ.Task.CHI) {
                this._lastPlayCardIndex = source.playerIndex;
            }
        }
    },

    /**
     * (可能导致挂起事件的)操作未导致挂起的结束回调
     * @param source
     */
    onHangupTaskNone: function (source) {
        if (source.task == MJ.Task.PLAY) {
            this._lastPlayCardIndex = source.playerIndex;
        }
    },

    dealHangupTaskTing: function (tasks) {
        logger.debug("room=%d dealHangupTaskTing tasks=%j", this.rid(), tasks);

        for (var i = 0; i < tasks.length; ++i) {
            var task = tasks[i];
            var player = this.getPlayerByIndex(task.playerIndex);
            player.setBaoTing();
            this.broadcastReplay("onPlayerBaoTing", {
                playerIndex: player.getIndex(),
            });
        }
    },

    dealHangupTaskHu: function (tasks) {
        logger.debug("room=%d dealHangupTaskHu tasks=%j", this.rid(), tasks);

        var room = this;
        var enableYPDX = this.isYPDXEnabled();

        var dpTargets = {}; // 点炮玩家坐标 => [胡牌玩家坐标数]
        for (var i = 0; i < tasks.length; ++i) {
            var huTask = tasks[i];
            if (huTask.playerIndex != huTask.from) {
                var dpIndex = huTask.from;
                if (!dpTargets[dpIndex]) {
                    var dpPlayer = this.getPlayerByIndex(dpIndex);
                    if (dpPlayer) {
                        dpPlayer.setLastPlayCardTask(MJ.Task.HU);
                    }
                    dpTargets[dpIndex] = [];
                }
                dpTargets[dpIndex].push(huTask);
            }
        }

        if (enableYPDX) {
            ExObject.eachKeyNum(dpTargets, function (eIndex, eHuTasks) {
                if (eHuTasks.length > 1) {
                    var ePlayer = this.getPlayerByIndex(eIndex);
                    ePlayer.setRoundScoreRule(MJ.ScoreRule.YPDX, eHuTasks.length);
                    eHuTasks.forEach(function (eHuTask) {
                        eHuTask.YPDX = eHuTasks.length;
                    });
                }
            }.bind(this));
        } else {
            var cancelHuIndexArr = [];
            ExObject.eachKeyNum(dpTargets, function (eIndex, eHuTasks) {
                if (eHuTasks.length > 1) {
                    var nearIndex = 0;
                    var nearDistance = 0;
                    eHuTasks.forEach(function (eHuTask) {
                        var eHuIndex = eHuTask.playerIndex;
                        var distance = room.getIndexDistance(eIndex, eHuIndex);
                        if (!nearIndex) {
                            nearIndex = eHuIndex;
                            nearDistance = distance;
                        } else {
                            if (distance < nearDistance) {
                                cancelHuIndexArr.push(nearIndex);
                                nearIndex = eHuIndex;
                                nearDistance = distance;
                            } else {
                                cancelHuIndexArr.push(eHuIndex);
                            }
                        }
                    });
                }
            }.bind(this));

            cancelHuIndexArr.forEach(function (eHuIndex) {
                for (var i = 0; i < tasks.length; ++i) {
                    var huTask = tasks[i];
                    if (huTask.playerIndex == eHuIndex) {
                        tasks.splice(i, 1);
                        break;
                    }
                }
            });
        }


        var farthestHuIndex = 0;
        var farthestHuDistance = 0;

        for (var i = 0; i < tasks.length; ++i) {
            var huTask = tasks[i];

            var huDistance = this.getIndexDistance(this.getCurPlay(), huTask.playerIndex);
            if (huDistance > farthestHuDistance) {
                farthestHuDistance = huDistance;
                farthestHuIndex = huTask.playerIndex;
            }

            this._huTasks.push(huTask);
            this.onSettlementHuTask(huTask);
        }

        if (this.checkHuSettlement()) {
            this.settlementCurRound(this.getRoomConfig().MJsettlementWait);
        } else {
            this._nextCurPlay(farthestHuIndex);
        }
    },

    dealHangupTaskGang: function (task) {
        logger.debug("room=%d onHangupTaskGang args=%j", this.rid(), task);
        var playerIndex = task.playerIndex;
        var player = this.getPlayerByIndex(playerIndex);

        var gangInfo = player.gangCard(task);
        if (!gangInfo) {
            return;
        }

        var card = gangInfo.card;
        var broadcastGangInfo = clone(gangInfo);
        broadcastGangInfo.playerIndex = playerIndex;

        player.setPassHu(false);
        //this._changeCurPlay(playerIndex, MJ.Task.GANG);

        if (this.checkGangTaskQG(task, gangInfo)) { // 可能被抢杠
            this._hangupTask.reset();
            this.checkGangHangupTask(playerIndex, gangInfo);  //===>hu ==> tijiao ==> new

            if (this._hangupTask.exist()) {
                broadcastGangInfo.existTask = true;
            }
            this.broadcastReplay("onPlayerGangCard", broadcastGangInfo);
            if(this.onAfterGangTask(player, card, task, gangInfo)) {
                this._changeCurPlay(playerIndex, MJ.Task.GANG);

                this._hangupTask.source.prevTask = MJ.Task.GANG;
                this.onCheckGangNewCard(true, gangInfo, player);
            } else {
                this._hangupTask.source.prevTask = MJ.Task.GANG;
            }
            // if (this.checkGangNewCard(gangInfo)) {
            //     logger.debug("room=%d sendHangupTask in=MJ.dealHangupTaskGang checkGangTaskQG=true checkGangNewCard=true", this.rid());
            //     this._sendHangupTask(this._dealNewCard, [player]);
            // } else {
            //     logger.debug("room=%d sendHangupTask in=MJ.dealHangupTaskGang checkGangTaskQG=true checkGangNewCard=false", this.rid());
            //     this._sendHangupTask(this._doCheckCurPlayHangupTask);
            // }
        } else {
            this.broadcastReplay("onPlayerGangCard", broadcastGangInfo);
            if (this.onAfterGangTask(player, card, task, gangInfo)) {
                this._changeCurPlay(playerIndex, MJ.Task.GANG);
                this.onCheckGangNewCard(false, gangInfo, player);
            }

            // if (this.checkGangNewCard(gangInfo)) { // 没有则发摸牌
            //     this._dealNewCard(player);
            // } else {
            //     this._doCheckCurPlayHangupTask();
            // }
        }
    },

    dealHangupTaskPeng: function (task) {
        logger.debug("room=%d onHangupTaskPeng args=%j", this.rid(), task);
        var playerIndex = task.playerIndex;
        var fromIndex = task.from;
        var card = task.card;

        var player = this.getPlayerByIndex(playerIndex);

        if (!player.pengCard(task)) {
            return;
        }

        var fromPlayer = this.getPlayerByIndex(fromIndex);
        if (fromPlayer) {
            fromPlayer.setLastPlayCardTask(MJ.Task.PENG);
        }

        this.broadcastReplay("onPlayerPengCard", {
            playerIndex: playerIndex,
            from: fromIndex,
            card: card
        });

        player.setPassHu(false);
        this._changeCurPlay(playerIndex, MJ.Task.PENG);
        this.onAfterPengTask(player, card, task);

        logger.debug("room=%d sendHangupTask in=MJ.dealHangupTaskPeng", this.rid());
        this._doCheckCurPlayHangupTask();
    },

    dealHangupTaskChi: function (task) {
        logger.debug("room=%d onHangupTaskChi args=%j", this.rid(), task);
        var playerIndex = task.playerIndex;
        var fromIndex = task.from;

        var player = this.getPlayerByIndex(playerIndex);
        if (!player.chiCard(task)) {
            return;
        }

        var fromPlayer = this.getPlayerByIndex(fromIndex);
        if (fromPlayer) {
            fromPlayer.setLastPlayCardTask(MJ.Task.CHI);
        }

        this.broadcastReplay("onPlayerChiCard", {
            playerIndex: playerIndex,
            from: fromIndex,
            card: task.card,
            selectCard: task.selectCard
        });

        player.setPassHu(false);
        this._changeCurPlay(playerIndex, MJ.Task.CHI);
        this.onAfterChiTask(player, task.card, task);


        logger.debug("room=%d sendHangupTask in=MJ.dealHangupTaskChi", this.rid());
        this._doCheckCurPlayHangupTask();
    },

    dealHangupTaskCustom: function (task) {
        logger.debug("room=%d dealHangupTaskCustom args=%j", this.rid(), task);

        var playerIndex = task.playerIndex;
        var player = this.getPlayerByIndex(playerIndex);

        this.onCustomHangupTask(player, task);
    },

    _doCheckCurPlayHangupTask: function () {
        this._hangupTask.reset();
        this.checkCurPlayHangupTask();
        this._sendHangupTask();
    },

    _doCheckNewCardHangupTask: function () {
        this._hangupTask.reset();
        this.checkNewCardHangupTask();
        this._sendHangupTask();
    },

    /**
     * 发送公牌数量
     * @private
     */
    _sendPublicCardChanged: function () {
        this.broadcast("onPublicCardChanged", {
            "cardNum": this._publicCards.getRemainNum(),
        });
    },

    /**
     * 发送挂起任务
     * @private
     */
    _sendHangupTask: function (passHandler, passHandlerArgs) {
        this._hangupTask.source.playerIndex = this._curPlay;
        this._hangupTask.source.task = this._curTask;
        if (passHandler) {
            this._hangupTask.passHandler = passHandler;
            this._hangupTask.passHandlerArgs = passHandlerArgs || [];
        }

        var now = Date.getStamp();
        this._hangupTask.enumUncommitted(function (eIndex, eTask) {
            var ePlayer = this.getPlayerByIndex(eIndex);
            if (ePlayer) {
                ePlayer.send("onHangupTask", {"task": eTask});
                ePlayer.setWaitHandleTime(now);
            }
        }.bind(this));

        this._hangupTask.check();
    },

    /**
     * 尝试切换到下一个玩家
     * @param startIndex 以此为起点找其下一个玩家
     * @private
     */
    _nextCurPlay: function (startIndex) {
        startIndex = startIndex || this.getCurPlay();

        var oldCurPlay = this.getCurPlay();
        var tmpIndex = startIndex;

        do {
            tmpIndex = this.getNextIndex(tmpIndex);
            var tmpPlayer = this.getPlayerByIndex(tmpIndex);
            if (tmpPlayer && this.isAfterHuContinuePlay(tmpPlayer)) {
                break;
            }
        } while (tmpIndex != startIndex);

        this._changeCurPlay(tmpIndex, 0, true);
        this.dealNewCard();
    },

    /**
     * 改变当前出牌玩家
     * @param playerIndex
     * @private
     */
    _changeCurPlay: function (playerIndex, task, newCard) {
        var oldCurPlay = this.getCurPlay();
        logger.debug("room=%d index=%d changeCurPlay old=%d task=%d", this.rid(), playerIndex, oldCurPlay, task);

        this._curPlay = playerIndex;
        this._curTask = task;
        this._curGang = (task == MJ.Task.GANG) ? (this._curGang + 1) : 0;

        if (this.getCurPlay() != oldCurPlay) {
            this._sendCurPlay(newCard);
        } else {
            this._sendCurPlay(newCard);
        }

        this.onAfterChangeCurPlay(oldCurPlay, newCard);

        this.getCurPlayer().setWaitHandleTime(this.getNowTime());
    },

    dealNewCard: function(player) {
        this._dealNewCard();
    },

    /**
     * 给当前玩家发一张牌
     * @private
     */
    _dealNewCard: function () {
        if (this.isCurRoundFinished()) {
            return;
        }

        this.onBeforeNewCard();
        if (this.isCurRoundFinished()) {
            return;
        }

        var player = this.getCurPlayer();

        if (this._publicCards.isEmpty()) {
            this.settlementCurRound(0);
            return;
        }

        var card = this._publicCards.getCard();
        player.addNewCard(card);


        player.setPassHu(false);
        this._curTask = 0;
        this._sendPublicCardChanged();

        this._hangupTask.reset();
        this.checkNewCardHangupTask();

        var resp = {
            playerIndex: player.getIndex(),
            card: card,
            remain: this._publicCards.getRemainNum()
        }

        if (this._hangupTask.existUncommitted(player.getIndex())) {
            resp.existHangup = true;
        }

        this.broadcastReplay("onNewCard", resp);

        logger.debug("room=%d sendHangupTask in=MJ._dealNewCard", this.rid());
        this._sendHangupTask(this.hangupEmptyHandler);

        this.onAfterNewCard();
    },

    /**
     * 广播当前出牌玩家
     * @private
     */
    _sendCurPlay: function (newCard) {
        var msg = {
            "curPlay": this.getCurPlay()
        };
        if (newCard) {
            msg.newCard = true;
            this._curPlayNewCard = true;
        } else {
            this._curPlayNewCard = false;
        }
        this.broadcastReplay("onCurPlay", msg);
    },
});